"""
再平衡管理

提供投资组合再平衡策略、触发条件、执行逻辑等功能。
"""

from typing import Dict, List, Optional, Any, Callable, Tuple
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from enum import Enum
import asyncio
from decimal import Decimal
import math

from .allocation import AllocationResult, AssetAllocator, AllocationMethod
from .portfolio import Portfolio


class RebalanceTrigger(Enum):
    """再平衡触发条件"""
    TIME_BASED = "time_based"  # 基于时间
    THRESHOLD_BASED = "threshold_based"  # 基于阈值
    VOLATILITY_BASED = "volatility_based"  # 基于波动率
    PERFORMANCE_BASED = "performance_based"  # 基于绩效
    RISK_BASED = "risk_based"  # 基于风险
    CUSTOM = "custom"  # 自定义


class RebalanceMethod(Enum):
    """再平衡方法"""
    FULL_REBALANCE = "full_rebalance"  # 完全再平衡
    PARTIAL_REBALANCE = "partial_rebalance"  # 部分再平衡
    THRESHOLD_REBALANCE = "threshold_rebalance"  # 阈值再平衡
    BAND_REBALANCE = "band_rebalance"  # 带状再平衡


@dataclass
class RebalanceRule:
    """再平衡规则"""
    rule_id: str
    name: str
    trigger: RebalanceTrigger
    method: RebalanceMethod
    
    # 时间触发参数
    frequency: Optional[str] = None  # daily, weekly, monthly, quarterly
    schedule_time: Optional[str] = None  # HH:MM格式
    
    # 阈值触发参数
    weight_threshold: float = 0.05  # 权重偏离阈值
    value_threshold: Optional[Decimal] = None  # 价值偏离阈值
    
    # 波动率触发参数
    volatility_threshold: float = 0.2  # 波动率阈值
    volatility_window: int = 20  # 波动率计算窗口
    
    # 风险触发参数
    risk_threshold: float = 0.15  # 风险阈值
    
    # 再平衡参数
    min_trade_amount: Decimal = Decimal('1000')  # 最小交易金额
    transaction_cost_rate: float = 0.001  # 交易成本率
    
    # 状态
    enabled: bool = True
    last_triggered: Optional[datetime] = None
    created_time: datetime = field(default_factory=datetime.now)


@dataclass
class RebalanceAction:
    """再平衡动作"""
    symbol: str
    current_weight: float
    target_weight: float
    current_value: Decimal
    target_value: Decimal
    trade_amount: Decimal  # 正数为买入，负数为卖出
    trade_shares: Decimal
    estimated_cost: Decimal


@dataclass
class RebalanceResult:
    """再平衡结果"""
    rebalance_id: str
    portfolio_id: str
    rule_id: str
    trigger_reason: str
    
    # 再平衡前状态
    before_weights: Dict[str, float]
    before_values: Dict[str, Decimal]
    before_total_value: Decimal
    
    # 再平衡后状态
    after_weights: Dict[str, float]
    after_values: Dict[str, Decimal]
    after_total_value: Decimal
    
    # 交易动作
    actions: List[RebalanceAction]
    total_trade_amount: Decimal
    estimated_total_cost: Decimal
    
    # 执行状态
    executed: bool = False
    execution_time: Optional[datetime] = None
    actual_cost: Optional[Decimal] = None
    
    created_time: datetime = field(default_factory=datetime.now)


class RebalanceStrategy:
    """
    再平衡策略基类
    
    定义再平衡的通用接口。
    """
    
    def __init__(self, name: str, method: RebalanceMethod):
        """初始化再平衡策略"""
        self.name = name
        self.method = method
    
    def should_rebalance(
        self,
        portfolio: Portfolio,
        target_allocation: AllocationResult,
        rule: RebalanceRule,
        **kwargs
    ) -> Tuple[bool, str]:
        """判断是否需要再平衡"""
        raise NotImplementedError("子类必须实现此方法")
    
    def calculate_rebalance_actions(
        self,
        portfolio: Portfolio,
        target_allocation: AllocationResult,
        rule: RebalanceRule,
        **kwargs
    ) -> List[RebalanceAction]:
        """计算再平衡动作"""
        raise NotImplementedError("子类必须实现此方法")


class ThresholdRebalanceStrategy(RebalanceStrategy):
    """阈值再平衡策略"""
    
    def __init__(self):
        super().__init__("阈值再平衡", RebalanceMethod.THRESHOLD_REBALANCE)
    
    def should_rebalance(
        self,
        portfolio: Portfolio,
        target_allocation: AllocationResult,
        rule: RebalanceRule,
        **kwargs
    ) -> Tuple[bool, str]:
        """判断是否需要再平衡"""
        current_weights = portfolio.get_all_weights()
        target_weights = target_allocation.weights
        
        max_deviation = 0.0
        max_deviation_symbol = ""
        
        # 检查权重偏离
        for symbol in target_weights:
            current_weight = current_weights.get(symbol, 0.0)
            target_weight = target_weights[symbol]
            deviation = abs(current_weight - target_weight)
            
            if deviation > max_deviation:
                max_deviation = deviation
                max_deviation_symbol = symbol
        
        if max_deviation > rule.weight_threshold:
            return True, f"权重偏离超过阈值: {max_deviation_symbol} 偏离 {max_deviation:.4f}"
        
        return False, "权重偏离未超过阈值"
    
    def calculate_rebalance_actions(
        self,
        portfolio: Portfolio,
        target_allocation: AllocationResult,
        rule: RebalanceRule,
        **kwargs
    ) -> List[RebalanceAction]:
        """计算再平衡动作"""
        actions = []
        current_weights = portfolio.get_all_weights()
        target_weights = target_allocation.weights
        total_value = portfolio.get_total_value()
        
        for symbol in target_weights:
            current_weight = current_weights.get(symbol, 0.0)
            target_weight = target_weights[symbol]
            
            # 只有偏离超过阈值才进行调整
            if abs(current_weight - target_weight) > rule.weight_threshold:
                current_value = total_value * Decimal(str(current_weight))
                target_value = total_value * Decimal(str(target_weight))
                trade_amount = target_value - current_value
                
                # 检查最小交易金额
                if abs(trade_amount) >= rule.min_trade_amount:
                    # 获取当前价格（简化处理）
                    holding = portfolio.get_holding(symbol)
                    current_price = holding.current_price if holding else Decimal('1')
                    
                    trade_shares = trade_amount / current_price if current_price > 0 else Decimal('0')
                    estimated_cost = abs(trade_amount) * Decimal(str(rule.transaction_cost_rate))
                    
                    action = RebalanceAction(
                        symbol=symbol,
                        current_weight=current_weight,
                        target_weight=target_weight,
                        current_value=current_value,
                        target_value=target_value,
                        trade_amount=trade_amount,
                        trade_shares=trade_shares,
                        estimated_cost=estimated_cost
                    )
                    actions.append(action)
        
        return actions


class FullRebalanceStrategy(RebalanceStrategy):
    """完全再平衡策略"""
    
    def __init__(self):
        super().__init__("完全再平衡", RebalanceMethod.FULL_REBALANCE)
    
    def should_rebalance(
        self,
        portfolio: Portfolio,
        target_allocation: AllocationResult,
        rule: RebalanceRule,
        **kwargs
    ) -> Tuple[bool, str]:
        """判断是否需要再平衡"""
        # 完全再平衡总是执行
        return True, "执行完全再平衡"
    
    def calculate_rebalance_actions(
        self,
        portfolio: Portfolio,
        target_allocation: AllocationResult,
        rule: RebalanceRule,
        **kwargs
    ) -> List[RebalanceAction]:
        """计算再平衡动作"""
        actions = []
        current_weights = portfolio.get_all_weights()
        target_weights = target_allocation.weights
        total_value = portfolio.get_total_value()
        
        for symbol in target_weights:
            current_weight = current_weights.get(symbol, 0.0)
            target_weight = target_weights[symbol]
            
            current_value = total_value * Decimal(str(current_weight))
            target_value = total_value * Decimal(str(target_weight))
            trade_amount = target_value - current_value
            
            if trade_amount != 0:
                # 获取当前价格
                holding = portfolio.get_holding(symbol)
                current_price = holding.current_price if holding else Decimal('1')
                
                trade_shares = trade_amount / current_price if current_price > 0 else Decimal('0')
                estimated_cost = abs(trade_amount) * Decimal(str(rule.transaction_cost_rate))
                
                action = RebalanceAction(
                    symbol=symbol,
                    current_weight=current_weight,
                    target_weight=target_weight,
                    current_value=current_value,
                    target_value=target_value,
                    trade_amount=trade_amount,
                    trade_shares=trade_shares,
                    estimated_cost=estimated_cost
                )
                actions.append(action)
        
        return actions


class TimeBasedRebalanceStrategy(RebalanceStrategy):
    """基于时间的再平衡策略"""
    
    def __init__(self):
        super().__init__("时间再平衡", RebalanceMethod.FULL_REBALANCE)
    
    def should_rebalance(
        self,
        portfolio: Portfolio,
        target_allocation: AllocationResult,
        rule: RebalanceRule,
        **kwargs
    ) -> Tuple[bool, str]:
        """判断是否需要再平衡"""
        if not rule.frequency:
            return False, "未设置再平衡频率"
        
        now = datetime.now()
        
        # 检查上次触发时间
        if rule.last_triggered:
            if rule.frequency == "daily":
                next_trigger = rule.last_triggered + timedelta(days=1)
            elif rule.frequency == "weekly":
                next_trigger = rule.last_triggered + timedelta(weeks=1)
            elif rule.frequency == "monthly":
                next_trigger = rule.last_triggered + timedelta(days=30)
            elif rule.frequency == "quarterly":
                next_trigger = rule.last_triggered + timedelta(days=90)
            else:
                return False, f"不支持的频率: {rule.frequency}"
            
            if now < next_trigger:
                return False, f"未到再平衡时间，下次触发: {next_trigger}"
        
        # 检查时间点
        if rule.schedule_time:
            try:
                hour, minute = map(int, rule.schedule_time.split(':'))
                target_time = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
                
                # 如果当前时间在目标时间的5分钟内，则触发
                if abs((now - target_time).total_seconds()) <= 300:
                    return True, f"到达预定再平衡时间: {rule.schedule_time}"
            except ValueError:
                return False, f"无效的时间格式: {rule.schedule_time}"
        
        return True, f"满足{rule.frequency}再平衡条件"
    
    def calculate_rebalance_actions(
        self,
        portfolio: Portfolio,
        target_allocation: AllocationResult,
        rule: RebalanceRule,
        **kwargs
    ) -> List[RebalanceAction]:
        """计算再平衡动作"""
        # 使用完全再平衡策略
        full_strategy = FullRebalanceStrategy()
        return full_strategy.calculate_rebalance_actions(portfolio, target_allocation, rule, **kwargs)


class RebalanceManager:
    """
    再平衡管理器
    
    管理再平衡规则，监控触发条件，执行再平衡操作。
    """
    
    def __init__(self, asset_allocator: AssetAllocator):
        """初始化再平衡管理器"""
        self.asset_allocator = asset_allocator
        self.rules: Dict[str, RebalanceRule] = {}
        self.strategies: Dict[RebalanceMethod, RebalanceStrategy] = {}
        self.results: List[RebalanceResult] = []
        
        # 注册默认策略
        self._register_default_strategies()
        
        # 回调函数
        self.on_rebalance_triggered_callbacks: List[Callable] = []
        self.on_rebalance_completed_callbacks: List[Callable] = []
    
    def _register_default_strategies(self) -> None:
        """注册默认策略"""
        self.strategies[RebalanceMethod.THRESHOLD_REBALANCE] = ThresholdRebalanceStrategy()
        self.strategies[RebalanceMethod.FULL_REBALANCE] = FullRebalanceStrategy()
        self.strategies[RebalanceMethod.PARTIAL_REBALANCE] = FullRebalanceStrategy()  # 暂时使用完全再平衡
        self.strategies[RebalanceMethod.BAND_REBALANCE] = ThresholdRebalanceStrategy()  # 暂时使用阈值再平衡
    
    def add_rule(self, rule: RebalanceRule) -> None:
        """添加再平衡规则"""
        self.rules[rule.rule_id] = rule
    
    def remove_rule(self, rule_id: str) -> None:
        """移除再平衡规则"""
        if rule_id in self.rules:
            del self.rules[rule_id]
    
    def enable_rule(self, rule_id: str) -> None:
        """启用规则"""
        if rule_id in self.rules:
            self.rules[rule_id].enabled = True
    
    def disable_rule(self, rule_id: str) -> None:
        """禁用规则"""
        if rule_id in self.rules:
            self.rules[rule_id].enabled = False
    
    def check_rebalance_triggers(
        self,
        portfolio: Portfolio,
        target_allocation: Optional[AllocationResult] = None
    ) -> List[Tuple[RebalanceRule, str]]:
        """检查再平衡触发条件"""
        triggered_rules = []
        
        # 使用当前配置或重新计算
        if target_allocation is None:
            target_allocation = self.asset_allocator.get_current_allocation()
        
        if target_allocation is None:
            return triggered_rules
        
        for rule in self.rules.values():
            if not rule.enabled:
                continue
            
            strategy = self.strategies.get(rule.method)
            if not strategy:
                continue
            
            try:
                should_trigger, reason = strategy.should_rebalance(
                    portfolio, target_allocation, rule
                )
                
                if should_trigger:
                    triggered_rules.append((rule, reason))
            except Exception as e:
                print(f"检查再平衡触发条件失败 {rule.rule_id}: {e}")
        
        return triggered_rules
    
    def execute_rebalance(
        self,
        portfolio: Portfolio,
        rule: RebalanceRule,
        target_allocation: Optional[AllocationResult] = None,
        dry_run: bool = False
    ) -> RebalanceResult:
        """执行再平衡"""
        if target_allocation is None:
            target_allocation = self.asset_allocator.get_current_allocation()
        
        if target_allocation is None:
            raise ValueError("没有可用的目标配置")
        
        strategy = self.strategies.get(rule.method)
        if not strategy:
            raise ValueError(f"不支持的再平衡方法: {rule.method}")
        
        # 记录再平衡前状态
        before_weights = portfolio.get_all_weights()
        before_values = {symbol: holding.market_value 
                        for symbol, holding in portfolio.holdings.items()}
        before_total_value = portfolio.get_total_value()
        
        # 计算再平衡动作
        actions = strategy.calculate_rebalance_actions(portfolio, target_allocation, rule)
        
        # 计算总交易金额和成本
        total_trade_amount = sum(abs(action.trade_amount) for action in actions)
        estimated_total_cost = sum(action.estimated_cost for action in actions)
        
        # 创建再平衡结果
        rebalance_id = f"rebalance_{portfolio.portfolio_id}_{datetime.now().timestamp()}"
        result = RebalanceResult(
            rebalance_id=rebalance_id,
            portfolio_id=portfolio.portfolio_id,
            rule_id=rule.rule_id,
            trigger_reason="手动执行",
            before_weights=before_weights,
            before_values=before_values,
            before_total_value=before_total_value,
            after_weights=target_allocation.weights,
            after_values={},  # 执行后更新
            after_total_value=before_total_value,  # 执行后更新
            actions=actions,
            total_trade_amount=total_trade_amount if total_trade_amount else Decimal('0'),
            estimated_total_cost=estimated_total_cost if estimated_total_cost else Decimal('0')
        )
        
        if not dry_run:
            # 执行交易动作（这里需要与交易系统集成）
            self._execute_trades(portfolio, actions)
            
            # 更新执行状态
            result.executed = True
            result.execution_time = datetime.now()
            result.after_values = {symbol: holding.market_value 
                                 for symbol, holding in portfolio.holdings.items()}
            result.after_total_value = portfolio.get_total_value()
            
            # 更新规则触发时间
            rule.last_triggered = datetime.now()
            
            # 触发完成回调
            for callback in self.on_rebalance_completed_callbacks:
                try:
                    callback(result)
                except Exception as e:
                    print(f"再平衡完成回调执行失败: {e}")
        
        # 保存结果
        self.results.append(result)
        
        return result
    
    def _execute_trades(self, portfolio: Portfolio, actions: List[RebalanceAction]) -> None:
        """执行交易动作"""
        # 这里应该与交易系统集成，实际执行买卖订单
        # 目前只是模拟执行
        for action in actions:
            if action.trade_amount > 0:
                # 买入
                portfolio.add_holding(
                    action.symbol,
                    action.trade_shares,
                    action.target_value / action.trade_shares if action.trade_shares > 0 else Decimal('0')
                )
            elif action.trade_amount < 0:
                # 卖出
                portfolio.remove_holding(
                    action.symbol,
                    abs(action.trade_shares),
                    action.current_value / abs(action.trade_shares) if action.trade_shares != 0 else Decimal('0')
                )
    
    def get_rebalance_history(
        self,
        portfolio_id: Optional[str] = None,
        limit: Optional[int] = None
    ) -> List[RebalanceResult]:
        """获取再平衡历史"""
        results = self.results
        
        if portfolio_id:
            results = [r for r in results if r.portfolio_id == portfolio_id]
        
        if limit:
            results = results[-limit:]
        
        return results
    
    def get_summary(self) -> Dict[str, Any]:
        """获取管理器摘要"""
        enabled_rules = [r for r in self.rules.values() if r.enabled]
        
        return {
            'total_rules': len(self.rules),
            'enabled_rules': len(enabled_rules),
            'total_rebalances': len(self.results),
            'successful_rebalances': len([r for r in self.results if r.executed]),
            'available_strategies': list(self.strategies.keys())
        }
    
    async def monitor_and_rebalance(
        self,
        portfolio: Portfolio,
        check_interval: int = 300  # 5分钟检查一次
    ) -> None:
        """监控并自动执行再平衡"""
        while True:
            try:
                triggered_rules = self.check_rebalance_triggers(portfolio)
                
                for rule, reason in triggered_rules:
                    print(f"触发再平衡规则 {rule.name}: {reason}")
                    
                    # 触发回调
                    for callback in self.on_rebalance_triggered_callbacks:
                        try:
                            callback(rule, reason)
                        except Exception as e:
                            print(f"再平衡触发回调执行失败: {e}")
                    
                    # 执行再平衡
                    try:
                        result = self.execute_rebalance(portfolio, rule)
                        print(f"再平衡执行完成: {result.rebalance_id}")
                    except Exception as e:
                        print(f"再平衡执行失败: {e}")
                
                await asyncio.sleep(check_interval)
                
            except Exception as e:
                print(f"监控再平衡失败: {e}")
                await asyncio.sleep(check_interval)